package lang.asm;

import lang.DocParser;
import model.code.CodeFileChunk;
import util.StringUtil;

/**
 * This parses documentation comments for assembly language.
 * @author Neil Dickson
 */
public class ASMDoc extends DocParser {
	public static final ASMDoc instance = new ASMDoc();

	public static final String	BOX_CHARS			= "+-=_\\/*#|;<>";
	public static final String	TERMINAL_BOX_CHARS	= "\u00B0\u00B1\u00B2\u00B3\u00B4\u00B9\u00BA\u00BB\u00BC\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00D9\u00DA\u00DB\u00DC\u00DD\u00DF";

	public static final String	CRLF				= "\r\n";
	public static final String	BEGIN_DOC			= ";********************************************************************************************************************************"+CRLF;
	public static final String	END_DOC				= BEGIN_DOC;
	public static final String	EMPTY_DOC_LINE		= ";*\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*"+CRLF;
	public static final String	BEGIN_DOC_LINE		= ";*\t";
	public static final String	END_DOC_LINE		= "*"+CRLF;

	public static final int		FINAL_TABSTOP		= 4+INNER_CHAR_LIMIT+1;

	private ASMDoc() {

	}

	/**
	 * Takes a raw comment and transforms it to a uniform format with the guess of type at index 0 and guess of name at index 1;
	 * @param originalDoc the raw comment text
	 * @return uniform format documentation, or null if determined to not be a doc comment, but no nulls in the String[]
	 */
	public String[] docify(String[] originalDoc) {
		// Trim comment markers
		for (int i=0;i<originalDoc.length;++i) {
			originalDoc[i] = ASMParse.instance.trimComment(originalDoc[i]);
		}
		// Trim any left box edge
		for (int i=0;i<originalDoc.length;++i) {
			originalDoc[i] = stripSpecialLeft(originalDoc[i]);
		}
		// Check for right edge
		boolean hasRightEdge = false;
		for (String line : originalDoc) {
			if (line.length() > 0) {
				hasRightEdge = isBoxChar(line.charAt(line.length() - 1));
				break;
			}
		}
		// Trim right edge if it looks like there is one
		if (hasRightEdge) {
			for (int i=0;i<originalDoc.length;++i) {
				originalDoc[i] = stripSpecialRight(originalDoc[i]);
			}
		}
		// The rest is language-independent
		return docifyCommon(originalDoc);
	}

	/**
	 * Creates a doc comment for the specified {@link CodeFileChunk}.
	 * @param chunk the item for which to create a doc comment
	 * @return the formatted doc comment
	 */
	public String createDocComment(CodeFileChunk chunk) {
		String[] doc = createDocCommentCommon(chunk);
		StringBuffer buf = new StringBuffer(BEGIN_DOC+EMPTY_DOC_LINE);
		for (String line : doc) {
			if (line.length()==0) {	// This is much faster for the case of an empty doc line (which is common)
				buf.append(EMPTY_DOC_LINE);
			}
			else {
				buf.append(BEGIN_DOC_LINE).append(line);
				int nTabs = Math.max(1,(FINAL_TABSTOP-(4+StringUtil.getUntabbedLength(line))+3)>>2);
				for (;nTabs>0;--nTabs) {
					buf.append('\t');
				}
				buf.append(END_DOC_LINE);
			}
		}
		buf.append(EMPTY_DOC_LINE).append(BEGIN_DOC);
		return buf.toString();
	}

	private String stripSpecialLeft(String s) {
		int i=0;
		while (i<s.length() && isBoxChar(s.charAt(i))) {
			++i;
		}
		return s.substring(i);
	}

	private String stripSpecialRight(String s) {
		int i=s.length()-1;
		while (i>=0 && isBoxChar(s.charAt(i))) {
			--i;
		}
		while (i>=0 && s.charAt(i)<=' ') {
			--i;
		}
		return s.substring(0,i+1);
	}

	private boolean isBoxChar(char c) {
		// The check against 0x80 is so that most characters don't need to be checked against TERMINAL_BOX_CHARS.
		return (BOX_CHARS.indexOf(c)>=0 || (c>=0x80 && TERMINAL_BOX_CHARS.indexOf(c)>=0));
	}
}
